package me.ivenapp.gomoku.slice;

import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.impl.ConfirmPopupView;
import me.ivenapp.gomoku.ChessPiece;
import me.ivenapp.gomoku.ResourceTable;
import me.ivenapp.gomoku.SliceBase;
import me.ivenapp.gomoku.component.GomokuBoard;
import me.ivenapp.gomoku.component.GridLayout;
import me.ivenapp.gomoku.component.TitleBar;
import ohos.aafwk.content.Intent;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.VectorElement;
import ohos.agp.utils.TextAlignment;
import ohos.data.distributed.common.*;
import ohos.data.distributed.user.SingleKvStore;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.zson.ZSONObject;

/**
 * 五子棋
 */
public class GomokuAbilitySlice extends SliceBase {
    private static final HiLogLabel TAG = new HiLogLabel(HiLog.LOG_APP, 0x1001, GomokuAbilitySlice.class.getSimpleName());
    public static final String PARAM_ROW_NUM = "PARAM_ROW_NUM";

    //分布式数据库相关
    private long gameId;
    private KvManager kvManager;
    private SingleKvStore singleKvStore;
    private final String STORE_ID = "GomokuAbility";
    //存储action的数据库文档ID。文档数据结构：{ actionName: "name", actionData: "data" }
    private final String DOC_ID_ACTION = "doc_id_action";
    //action文档的key
    private final String KEY_ACTION_NAME = "actionName";
    private final String KEY_ACTION_DATA = "actionData";
    private final String KEY_ACTION_TIMESTAMP = "timestamp"; //时间戳

    private boolean isMute;
    //获胜局数
    private int whiteWinNum = 0;
    private int blackWinNum = 0;
    private int gameMode = 0; //0：未选择游戏模式。1：表示人机模式；2：表示联机模式
    private int myPieceType;
    private String opponent;

    private Button btnBack;
    private GomokuBoard gomokuBoard;
    private ConfirmPopupView popGameWin;
    private int rowNum;

    @Override
    public void onStart(Intent intent) {
        HiLog.debug(TAG, "onStart");
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_gomoku);
        init(intent);
    }

    private void init(Intent intent) {
        whiteWinNum = 0;
        blackWinNum = 0;
        try {
            gameMode = intent.getIntParam(GomokuBoard.PARAM_GAME_MODE, GomokuBoard.MODE_AI);
            myPieceType = intent.getIntParam(GomokuBoard.PARAM_PIECE_TYPE, GomokuBoard.PIECE_TYPE_WHITE);
            opponent = intent.getStringParam(GomokuBoard.PARAM_OPPONENT);
            rowNum = intent.getIntParam(PARAM_ROW_NUM, 10);

            Intent resultData = new Intent();
            intent.setParam(GomokuBoard.PARAM_GAME_MODE, gameMode);
            setResult(resultData);

            if(opponent == null || "".equals(opponent)) {
                opponent = "电脑";
            }

            if(gameMode == GomokuBoard.MODE_ONLINE) {
                resultData = new Intent();
                resultData.setParam(GomokuBoard.PARAM_GAME_MODE, gameMode);
                setResult(intent);

                gameId = intent.getLongParam("gameId", 0);
//                HiLog.debug(TAG, "gameId=" + gameId);
//                DOC_ID_ACTION = DOC_ID_ACTION + gameId;
                initDatabase();
            }

            initUI();
            startGame(gameMode, myPieceType);
        } catch (Exception e) {
            HiLog.error(TAG, e.getMessage());
        }
    }

    private void initDatabase() {
        // 创建分布式数据库管理对象
        KvManagerConfig config = new KvManagerConfig(this);
        kvManager = KvManagerFactory.getInstance().createKvManager(config);
        // 创建分布式数据库
        Options options = new Options();
        options.setCreateIfMissing(true).setAutoSync(true).setEncrypt(false).setKvStoreType(KvStoreType.SINGLE_VERSION);
        try {
            singleKvStore = kvManager.getKvStore(options, STORE_ID);
        } catch (KvStoreException e) {
            HiLog.warn(TAG,  "getKvStore:" + e.getKvStoreErrorCode());
        }

        if(singleKvStore != null) {
            singleKvStore.delete(DOC_ID_ACTION);
            //监听数据变化，更新界面
            KvStoreObserverClient kvStoreObserverClient = new KvStoreObserverClient();
            kvStoreObserverClient.setDataChangedListener(() -> {
                try {
                    String doc = singleKvStore.getString(DOC_ID_ACTION);
//                    HiLog.debug(TAG, "DocId=" + DOC_ID_ACTION);
                    if(doc == null || "".equals(doc)) {
                        return;
                    }
                    ZSONObject dataObj = ZSONObject.stringToZSON(doc);
                    String action = dataObj.getString(KEY_ACTION_NAME);
                    if(action == null || "".equals(action)) {
                        return;
                    }
//                    HiLog.debug(TAG, "action=" + action);
                    getUITaskDispatcher().asyncDispatch(()->{
                        if(GomokuBoard.ACTION_START_GAME.equals(action)) {
                            startGame(gameMode, myPieceType);
                        } else if(GomokuBoard.ACTION_PUT_PIECE.equals(action)) {
                            ChessPiece piece = ZSONObject.stringToClass(dataObj.getString(KEY_ACTION_DATA), ChessPiece.class);
//                            HiLog.debug(TAG, "put piece request. pieceType=" + piece.getType());
                            gomokuBoard.putPiece(piece);
                        } else if(GomokuBoard.ACTION_BACK_STEP.equals(action)) {
                            int pieceType = dataObj.getIntValue(KEY_ACTION_DATA);
                            gomokuBoard.backStep(pieceType);
                        } else if(GomokuBoard.ACTION_REPLAY.equals(action)) {
                            btnBack.setEnabled(false);
                            gomokuBoard.setReplay();
                            if(popGameWin != null && !popGameWin.isDismiss()) {
                                popGameWin.dismiss();
                            }
                        } else if(GomokuBoard.ACTION_EXIT.equals(action)) {
                            terminate();
                        }
                    });
                } catch (Exception e) {
                    //
                }
            });
            // 订阅分布式数据变化
            try {
                singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, kvStoreObserverClient);
            } catch (Exception e) {
                HiLog.error(TAG, e.getMessage());
            }
        }
    }

    private void sendAction(ZSONObject param) {
        if(singleKvStore == null) return;
        try {
            singleKvStore.putString(DOC_ID_ACTION, param.toString());
        } catch (KvStoreException e) {
            HiLog.warn(TAG, "putString:" + e.getKvStoreErrorCode());
        }
    }

    protected void initUI() {
        TitleBar titleBar = (TitleBar) findComponentById(ResourceTable.Id_title_bar);
        titleBar.setLeftIconClickedListener(component -> exitGame());
        titleBar.addActionIcon(new Element[] { new VectorElement(this, ResourceTable.Graphic_ic_public_more_filled)}, (component, i) -> {
            if(i == 0) {
                String volText = isMute ? "打开音效" : "关闭音效";
                new XPopup.Builder(getContext())
                        .atView(component)
                        .asAttachList(new String[]{ volText, "玩法帮助"}, null, (i1, s) -> {
                            if(i1 == 0) {
                                isMute = !isMute;
                                if(gomokuBoard != null) {
                                    gomokuBoard.setMute(isMute);
                                }
                            } else if(i1 == 1) {
                                showHelper();
                            }
                        }).show();
            }
        });
        if(gameMode == GomokuBoard.MODE_AI) {
            titleBar.setTitle(getString(ResourceTable.String_mode_ai));
        } else if(gameMode == GomokuBoard.MODE_ONLINE) {
            titleBar.setTitle(getString(ResourceTable.String_mode_online));
        } else if(gameMode == GomokuBoard.MODE_SELF) {
            titleBar.setTitle(getString(ResourceTable.String_mode_self));
        }

        Component cntMain = findComponentById(ResourceTable.Id_cnt_main);
        setGridLayout(cntMain);

        setPlayers();
        //TODO 【笔记】使用present进行slice间跳转，Slice也是堆栈模式，需重新设置一下页面组件的值
        setScore();


        //重新开始|再来一局 按钮
        findComponentById(ResourceTable.Id_btn_start).setClickedListener(component -> {
            if(gameMode != GomokuBoard.MODE_ONLINE) {
                startGame(gameMode, myPieceType);
            } else {
                ZSONObject dataObj = new ZSONObject();
                dataObj.put(KEY_ACTION_NAME, GomokuBoard.ACTION_START_GAME);
                dataObj.put(KEY_ACTION_TIMESTAMP, System.currentTimeMillis()); //加上时间戳，防止数据无变化
                sendAction(dataObj);
            }
            Button btnStart = (Button) findComponentById(ResourceTable.Id_btn_start);
            btnStart.setText("重新开始");
        });

        //悔棋按钮
        btnBack = (Button) findComponentById(ResourceTable.Id_btn_back);
        btnBack.setClickedListener(component -> {
            if(gameMode != GomokuBoard.MODE_ONLINE) {
                gomokuBoard.backStep(myPieceType);
            } else {
                ZSONObject dataObj = new ZSONObject();
                dataObj.put(KEY_ACTION_NAME, GomokuBoard.ACTION_BACK_STEP);
                dataObj.put(KEY_ACTION_DATA, myPieceType);
                dataObj.put(KEY_ACTION_TIMESTAMP, System.currentTimeMillis());
                sendAction(dataObj);
            }
        });

        //退出游戏按钮
        findComponentById(ResourceTable.Id_btn_exit).setClickedListener(component -> {
            exitGame();
        });

        gomokuBoard = (GomokuBoard) findComponentById(ResourceTable.Id_gomoku);
        gomokuBoard.setMute(isMute);
        gomokuBoard.setWinListener(winner -> {
            String strWinner;
            if(winner == 1) {
                strWinner = "白棋获胜";
                whiteWinNum++;
                setScore();
            } else if(winner == 2) {
                strWinner = "黑棋获胜";
                blackWinNum++;
                setScore();
            } else {
                strWinner = "未分胜负";
            }

            popGameWin = new XPopup.Builder(this)
                    .autoDismiss(true)
                    .asConfirm("本局结束", strWinner, "取消", "复盘", () -> {
                        if(gameMode != GomokuBoard.MODE_ONLINE) {
                            gomokuBoard.setReplay();
                        } else {
                            ZSONObject dataObj = new ZSONObject();
                            dataObj.put(KEY_ACTION_NAME, GomokuBoard.ACTION_REPLAY);
                            sendAction(dataObj);
                        }
                    }, null, false);
            popGameWin.show();

            btnBack.setEnabled(false);
            Button btnStart = (Button) findComponentById(ResourceTable.Id_btn_start);
            btnStart.setText("再来一局");
        });
        if(gameMode == GomokuBoard.MODE_ONLINE) {
            //TODO 【笔记】使用present进行slice间跳转，页面组件有缓存？
            //问题复现：退出游戏，再开始联机游戏，setPutPieceListener事件里的 DOC_ID_ACTION 没变，还是前一次游戏的值
            //原因：gomokuBoard 缓存了上一次设置的 listener
            //解决办法：每次进入都重新设置下 listener
//            if(gomokuBoard.getPutPieceListener() == null) {
                //联机模式下，通过更新数据库的方式来通知本地和远程应用放置棋子
                gomokuBoard.setPutPieceListener(chessPiece -> {
                    if(singleKvStore == null || chessPiece == null) return;
                    ZSONObject dataObj = new ZSONObject();
                    dataObj.put(KEY_ACTION_NAME, GomokuBoard.ACTION_PUT_PIECE);
                    ZSONObject piece = ZSONObject.classToZSON(chessPiece);
                    dataObj.put(KEY_ACTION_DATA, piece.toString());
                    dataObj.put(KEY_ACTION_TIMESTAMP, System.currentTimeMillis());
                    try {
                        singleKvStore.putString(DOC_ID_ACTION, dataObj.toString());
                    } catch (KvStoreException e) {
                        HiLog.warn(TAG, "putString:" + e.getKvStoreErrorCode());
                    }
                });
            }
//        }
        //有缓存，刷新下布局
        gomokuBoard.postLayout();
    }

    //开始游戏
    private void startGame(int gameMode, int myPieceType) {
        try {
            HiLog.debug(TAG, "startGame. gameMode=" + gameMode + ", myPieceType=" + myPieceType);
            //todo 【bug】五子棋联机模式游戏中点击重新开始闪退
            //联机时点击退出游戏，再联机，游戏中点击重新开始，偶尔出现在null object上调用 findComponentById 的错误
            //推测是不是在UI线程中调用 findComponentById，会偶尔获取不到 Slice 对象
            //触发两次startGame调用
//            findComponentById(ResourceTable.Id_btn_back).setEnabled(true);

            btnBack.setEnabled(true);
            gomokuBoard.setRowNum(rowNum);
            gomokuBoard.startGame(gameMode, myPieceType);
        } catch (Exception e) {
            HiLog.debug(TAG, e.getMessage());
        }
    }

    //退出游戏
    private void exitGame() {
        if(gameMode != GomokuBoard.MODE_ONLINE) {
            terminate();
        } else {
            ZSONObject dataObj = new ZSONObject();
            dataObj.put(KEY_ACTION_NAME, GomokuBoard.ACTION_EXIT);
            dataObj.put(KEY_ACTION_TIMESTAMP, System.currentTimeMillis());
            sendAction(dataObj);
        }
    }

    private void setPlayers() {
        Text txt = (Text) findComponentById(ResourceTable.Id_txt_opponent_name);
        txt.setText(opponent);

        ShapeElement bkWhite = new ShapeElement();
        bkWhite.setShape(ShapeElement.OVAL);
        bkWhite.setRgbColor(RgbColor.fromArgbInt(0xFFFFFFFF));
        ShapeElement bkBlack = new ShapeElement();
        bkBlack.setShape(ShapeElement.OVAL);
        bkBlack.setRgbColor(RgbColor.fromArgbInt(0xFF000000));

        Component myChess = findComponentById(ResourceTable.Id_my_chess);
        Component opponentChess = findComponentById(ResourceTable.Id_opponent_chess);
        if(myPieceType == GomokuBoard.PIECE_TYPE_WHITE) {
            myChess.setBackground(bkWhite);
            opponentChess.setBackground(bkBlack);
        } else {
            opponentChess.setBackground(bkWhite);
            myChess.setBackground(bkBlack);
        }
    }

    private void setScore() {
        Text text = (Text) findComponentById(ResourceTable.Id_txt_score);
        if(myPieceType == GomokuBoard.PIECE_TYPE_WHITE) {
            text.setText(whiteWinNum + " : " + blackWinNum);
        } else {
            text.setText(blackWinNum + " : " + whiteWinNum);
        }
    }

    static class KvStoreObserverClient implements KvStoreObserver {
        private IDataChangedListener listener;

        public void setDataChangedListener(IDataChangedListener listener) {
            this.listener = listener;
        }

        @Override
        public void onChange(ChangeNotification notification) {
//            List<Entry> insertEntries = notification.getInsertEntries();
//            List<Entry> updateEntries = notification.getUpdateEntries();
//            List<Entry> deleteEntries = notification.getDeleteEntries();
            if(listener != null) {
                listener.onChanged();
            }
        }
    }

    interface IDataChangedListener {
        void onChanged();
    }

    private void showHelper(){
        String strHelp = "1. 带加号的棋子为最后一步棋子，带点的棋子为倒数第二步棋子。" + "\n\n"
                + "2. 联机对战需要两台设备登录同一华为帐号并打开WIFI和蓝牙。" + "\n\n"
                + "3. 复盘。复盘模式下点击棋盘上的棋子会降低后续棋子的可见度，并高亮前后棋子的序号，方便观察棋局。";
        ConfirmPopupView view = new XPopup.Builder(this)
                .asConfirm("玩法帮助", strHelp, "取消", null, null, null, true);
        Text text = view.getContentTextView();
        text.setTextSize(AttrHelper.fp2px(14, this));
        text.setTextAlignment(TextAlignment.LEFT);
        view.show();
    }

    @Override
    protected void onStop() {
        super.onStop();
        try {
            kvManager.closeKvStore(singleKvStore);
        } catch (Exception e) {
            HiLog.warn(TAG, e.getMessage());
        }
    }
}
